using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace System.Windows.Controls
{
    ///<summary>
    /// Represents collection of nested items for <see cref="TreeGrid"/> and <see cref="TreeGridItem"/>
    ///</summary>
    public class TreeGridItemCollection : IList<TreeGridItem>, INotifyCollectionChanged
    {
        internal TreeGridItemCollection()
        {
        }

        internal TreeGridItemCollection(TreeGridItem parentItem)
        {
            _parentItem = parentItem;
        }

        private TreeGrid _parentGrid;

        internal void SetParentGrid(TreeGrid grid)
        {
            if (_parentGrid == grid) return;
            foreach (var item in _items)
                item.SetParentGrid(grid);
            _parentGrid = grid;
        }

        private readonly TreeGridItem _parentItem;

        private readonly List<TreeGridItem> _items = new List<TreeGridItem>();

        public IEnumerator<TreeGridItem> GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Add(TreeGridItem item)
        {
            if (IsReadOnly) throw new InvalidOperationException();
            AddInt(item);
        }

        internal void AddInt(TreeGridItem item)
        {
            if (_items.Any(e => e.Data == item.Data)) return;
            _items.Add(item);
            item.SetParentItem(_parentItem);
            item.SetParentGrid(_parentGrid);
            //if (_parentGrid != null)
            //    _parentGrid.InvokeItemConnect(item);
            InvokeCollectionChanged(new NotifyCollectionChangedEventArgs(
                                        NotifyCollectionChangedAction.Add, item, _items.Count - 1));
        }

        public void Clear()
        {
            if (IsReadOnly) throw new InvalidOperationException();
            ClearInt();
        }

        internal void ClearInt()
        {
            foreach (var item in _items)
            {
                item.SetParentGrid(null);
                item.SetParentItem(null);
            }
            //if (_parentGrid != null)
            //    foreach (var item in _items)
            //        _parentGrid.InvokeItemDisconnect(item);
            _items.Clear();
            InvokeCollectionChanged(new NotifyCollectionChangedEventArgs(
                                        NotifyCollectionChangedAction.Reset));
        }

        public bool Contains(TreeGridItem item)
        {
            return _items.Contains(item);
        }

        public void CopyTo(TreeGridItem[] array, int arrayIndex)
        {
            _items.CopyTo(array, arrayIndex);
        }

        public bool Remove(TreeGridItem item)
        {
            if (IsReadOnly) throw new InvalidOperationException();
            return RemoveInt(item);
        }

        internal bool RemoveInt(TreeGridItem item)
        {
            var index = _items.IndexOf(item);
            var res = RemoveSilent(item);
            //if (res && _parentGrid != null)
            //    _parentGrid.InvokeItemDisconnect(item);
            if (res)
                InvokeCollectionChanged(new NotifyCollectionChangedEventArgs(
                                            NotifyCollectionChangedAction.Remove, item, index));
            return res;
        }

        internal bool RemoveSilent(TreeGridItem item)
        {
            var res = _items.Remove(item);
            if (!res) return false;
            item.SetParentGrid(null);
            item.SetParentItem(null);
            return true;
        }

        public int Count
        {
            get { return _items.Count; }
        }

        public bool IsReadOnly
        {
            get
            {
                return _parentItem == null
                           ? _parentGrid.ItemsSource != null
                           : _parentItem.ItemsSource != null;
            }
        }

        public int IndexOf(TreeGridItem item)
        {
            return _items.IndexOf(item);
        }

        public void Insert(int index, TreeGridItem item)
        {
            if (IsReadOnly) throw new InvalidOperationException();
            InsertInt(index, item);
        }

        internal void InsertInt(int index, TreeGridItem item)
        {
            InsertSilent(index, item);
            //if (_parentGrid != null)
            //    _parentGrid.InvokeItemConnect(item);
        }

        internal void InsertSilent(int index, TreeGridItem item)
        {
            if (_items.Any(e => e.Data == item.Data)) return;
            _items.Insert(index, item);
            item.SetParentItem(_parentItem);
            item.SetParentGrid(_parentGrid);
            InvokeCollectionChanged(new NotifyCollectionChangedEventArgs(
                                        NotifyCollectionChangedAction.Add, item, index));
        }

        public void RemoveAt(int index)
        {
            if (IsReadOnly) throw new InvalidOperationException();
            RemoveAtInt(index);
        }

        internal void RemoveAtInt(int index)
        {
            var item = _items[index];
            _items.RemoveAt(index);
            item.SetParentGrid(null);
            item.SetParentItem(null);
            //if (_parentGrid != null)
            //    _parentGrid.InvokeItemDisconnect(item);
            InvokeCollectionChanged(new NotifyCollectionChangedEventArgs(
                                        NotifyCollectionChangedAction.Remove, item, index));
        }

        TreeGridItem IList<TreeGridItem>.this[int index]
        {
            get { return this[index]; }
            set { throw new NotSupportedException(); }
        }

        /// <summary>
        /// Gets the element at the specified index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public TreeGridItem this[int index]
        {
            get { return _items[index]; }
        }

        /// <summary>
        /// Occurs when the items list of the collection has changed, or the collection is reset
        /// </summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        private void InvokeCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handler = CollectionChanged;
            if (handler != null) handler(this, e);
        }

        internal TreeGridItem GetItemForData(object data)
        {
            return _items.FirstOrDefault(item => item.Data == data) ??
                   _items.Select(item => item.Items.GetItemForData(data)).FirstOrDefault(res => res != null);
        }

        internal TreeGridItem GetItemForRow(DataGridRow row)
        {
            return _items.FirstOrDefault(item => item.Row == row) ??
                   _items.Select(item => item.Items.GetItemForRow(row)).FirstOrDefault(res => res != null);
        }

        internal int IndexOfAdv(TreeGridItem item)
        {
            var index = IndexOf(item);
            if (index != -1) return index;
            for (var i = 0; i < Count; i++)
            {
                index = _items[i].Items.IndexOfAdv(item);
                if (index != -1) return index + i;
            }
            return -1;
        }
    }
}